﻿using System;
using System.Collections.Specialized;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace ACServerApi.Extensions
{
    /// <summary>
    /// Name of the element written in thr INI file
    /// </summary>
    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Class)]
    public sealed class IniSettingsAttribute : Attribute
    {
        public readonly string Name;
        private string output;
        public string Output
        {
            get { return output; }
            set { output = value; }
        }

        public IniSettingsAttribute(string name)
        {
            this.Name = name;
        }
    }

    /// <summary>
    /// Interface for targeting extension methods to write INI files
    /// </summary>
    public interface IIniSerializable
    {

    }

    public interface IIniCollectionItem
    {
        string Name { get; set; }

        string ToString();
    }


    public static class IniSerializer
    {
        /// <summary>
        /// Writes an Object in INI format to the given stream
        /// </summary>
        /// <param name="writable">An object which implements "IIniSerializable"</param>
        public static string SerializeToIni(this IIniSerializable writable)
        {
            return SerializeToIni(writable, true);
        }

        ///<inheritdoc />
        public static string SerializeToIni(this IIniSerializable writable, bool writeComments)
        {
            if (writable == null)
            {
                throw new ArgumentNullException("writable");
            }
            StringBuilder output = new StringBuilder();

            Type writableType = writable.GetType();

            var categoryAttribute = writableType.GetCustomAttribute<IniSettingsAttribute>();
            string category = (categoryAttribute == null) ? string.Empty : categoryAttribute.Name;

            PropertyInfo[] members = writableType.GetProperties();

            if (!string.IsNullOrEmpty(category))
            {
                output.AppendLine("[" + category + "]");
            }
            string itemName = "";
            string itemOutput = "";
            string itemDescription = "";
            StringBuilder itemLine = new StringBuilder();
            string itemValue = "0";

            foreach (var item in members)
            {
                var propertyValue = item.GetValue(writable);

                if (propertyValue == null)
                {
                    propertyValue = "";
                }

                var iniset = item.GetCustomAttribute<IniSettingsAttribute>();
                itemName = iniset == null ? item.Name : iniset.Name;
                itemOutput = iniset == null ? "" : iniset.Output;
                itemDescription = item.GetCustomAttribute<DescriptionAttribute>() == null ? "" : item.GetCustomAttribute<DescriptionAttribute>().Description;

                var pv = propertyValue as IEnumerable<IIniCollectionItem>;
                if (pv != null)
                {
                    itemValue = pv.Serialize(itemOutput);
                }
                else
                {
                    itemValue = item.PropertyType.Equals(typeof(Boolean)) ? ((bool)propertyValue ? "1" : "0") : (propertyValue ?? "").ToString();
                }

                itemLine.AppendFormat("{0}={1}", itemName, itemValue);

                if (writeComments && !string.IsNullOrEmpty(itemDescription))
                {
                    itemLine.AppendFormat(" ; {0}", itemDescription);
                }
                output.AppendLine(itemLine.ToString());
                itemLine.Clear();
            }

            return output.ToString();
        }


        public static string Serialize(this IEnumerable<IIniCollectionItem> collection)
        {
            return Serialize(collection, "", ";");
        }
        public static string Serialize(this IEnumerable<IIniCollectionItem> collection, string outputMember)
        {
            return Serialize(collection, outputMember, ";");
        }
        public static string Serialize(this IEnumerable<IIniCollectionItem> collection, string outputMember, string separator)
        {
            if (string.IsNullOrWhiteSpace(outputMember))
            {
                return string.Join(separator, collection.Select(i => i.ToString()));
            }
            else
            {
                return string.Join(separator, collection.Select(i => GetOutput(outputMember, i)));
            }
        }

        private static string GetOutput(string outputMember, IIniCollectionItem i)
        {
            Type t = i.GetType();
            var x = t.GetProperty(outputMember).GetValue(i).ToString();
            return x;
        }
    }
}